Option Explicit

'################################################################
' Discriptor
' 维持插入顺序的HashSet ISet 接口实现
'################################################################

Private Const HASH_CODE_RANDOM_SIZE As Integer = 15

Implements ISet

'################################################################
' Field
'################################################################
' set 缓存
' Map<Val, hashCode>
Private cache As Object
' 顺序缓存
' Collection <value=Val, key=hashCode>
Private sortedCache As Collection

'################################################################
' Constructor
'################################################################
Private Sub Class_Initialize()
    Set cache = UtilsCreateSysObj.createMap
    Set sortedCache = New Collection
End Sub

Private Sub Class_Terminate()
    Set cache = Nothing
    Set sortedCache = Nothing
End Sub

'################################################################
' Property
'################################################################
' 获取 set 内元素的数量
Public Property Get ISet_Count() As Long
    ISet_Count = cache.Count
End Property

'################################################################
' Function
'################################################################
' 按照索引来获取 set 内元素的数量
' 索引从 0 开始
Public Function ISet_Item(ByRef index as Variant) As Variant
On Error GoTo catch
    ISet_Item = sortedCache(Int(index) + 1)
    Exit Function
catch:
    Set ISet_Item = sortedCache(Int(index) + 1)
End Function

' 向 set 内添加元素。不会重复添加
Public Sub ISet_Add(ByRef val As Variant)
    ' 只在缓存中不存在指定元素时，才添加
    If cache.Exists(val) = False Then
        ' 1. 计算hash值
        Dim hashCodeStr As String
        hashCodeStr = UtilsRandom.enNumStr(HASH_CODE_RANDOM_SIZE)

        ' 2. 防止碰撞
whileStart:
        While True
On Error GoTo catch
            ' 如发生碰撞（能够从顺序缓存中获取到），则生成一个新的 hash 值
            sortedCache.Item hashCodeStr
            hashCodeStr = UtilsRandom.enNumStr(HASH_CODE_RANDOM_SIZE)
            Goto whileStart
catch:
            Err.Clear
            Resume whileEnd
        Wend
whileEnd:

        ' 2. 保存到 set 缓存中
        cache.Add val, hashCodeStr

        ' 3. 保存到顺序缓存, key = hash值
        sortedCache.Add val, hashCodeStr
    End If
End Sub

' 检查 set 内是否存在指定元素
Public Function ISet_Exists(ByRef val As Variant) As Boolean
    ISet_Exists = cache.Exists(val)
End Function

' 从 set 内获取包含所有元素的数组
' 索引从 1 开始
Public Function ISet_Items() As Variant()
    ISet_Items = UtilsCollection.toVariantArray(sortedCache)
End Function

' 删除 set 内的指定元素
Public Sub ISet_Remove(ByRef val As Variant)
    If cache.Exists(val) = True Then
        ' 1. 获取hash值
        Dim hashCodeStr As String
        hashCodeStr = cache.Item(val)

        ' 2. 从 set 缓存中删除
        cache.Remove val

        ' 3. 从排序缓存中删除
        sortedCache.Remove hashCodeStr
    End If
End Sub

' 删除 set 内的所有元素
Public Sub ISet_Clear()
    ' 1. 清空 set 缓存
    cache.RemoveAll

    ' 2. 清空排序缓存
    UtilsCollection.removeAllCollection sortedCache
End Sub

' 克隆一个set
Public Function ISet_Clone() As ISet
    Dim result As LinkedHashSet
    Set result = New LinkedHashSet
    Dim v As Variant
    For Each v In cache.Keys
        result.ISet_Add v
    Next

    Set ISet_Clone = result
End Function

' 向 Set 中添加一个数组
' @param arr 数组
Public Sub ISet_AddArray(ByRef arr As Variant)
    Dim i As Integer
    For i = LBound(arr) To UBound(arr)
        ISet_Add arr(i)
    Next
End Sub

' 使用指定的分隔符，连接 set 内的所有元素
' 如果某个元素是一个 Object 类型的对象，无法生成字符串，只会为这个对象写入一个空格
' @param delimiter 分隔符
Public Function ISet_Join(ByRef delimiter As String) As String
    ISet_Join = UtilsArray.joinElem(cache.Keys, delimiter)
End Function

' 合并另一个 Set
' @param s 另一个 Set. 如果 s 和当前 Set 相同，将会立刻结束
Public Sub ISet_MergeSet(ByRef s As ISet)
    ' 1. 快速结束
    ' 1.1 如果 s 是空对象，则立刻结束
    If s Is Nothing Then
        Exit Sub
    End If
    ' 1.2 如果 s 和当前 Set 相同，将会立刻结束
    If s Is Me Then
        Exit Sub
    End If

    ' 2. 合并数据
    Dim v As Variant
    For Each v In s.Items
        ISet_Add v
    Next
End Sub

' 从当前 set 中删除和另一个 Set 中相同的元素
' @param s 另一个 Set. 如果 s 和当前 Set 相同，将会直接清空当前 Set
Public Sub ISet_RemoveSet(ByRef s As ISet)
    ' 1. 快速结束
    ' 1.1 如果 s 是空对象，则立刻结束
    If s Is Nothing Then
        Exit Sub
    End If

    ' 1.2 如果 s 和当前 Set 相同，将会直接清空
    If s Is Me Then
        ISet_Clear
        Exit Sub
    End If

    ' 2. 删除数据
    Dim v As Variant
    For Each v In s.Items
        ISet_Remove v
    Next
End Sub

' ==================================
' 处理数组引用
' ==================================
' 合并 IObjArrayRef 对象中的所有元素
Public Sub ISet_mergeObjArrayRef(ByRef arr As IObjArrayRef)
    ' 1. 快速结束
    ' 1.1 如果 arr 是空对象，则立刻结束
    If arr Is Nothing Then
        Exit Sub
    End If

    Dim arrMaxIndex As Long
    arrMaxIndex = arr.MaxIndex
    If arrMaxIndex = -1 Then
        Exit Sub
    End If

    ' 2. 合并数据
    Dim v As Variant
    Dim i As Long
    With arr
        For i = 0 To arrMaxIndex
            ISet_Add .Item(i)
        Next
    End With
End Sub

' 合并 IPrimitiveArrayRef 对象中的所有元素
Public Sub ISet_mergePrimitiveArrayRef(ByRef arr As IPrimitiveArrayRef)
    ' 1. 快速结束
    ' 1.1 如果 arr 是空对象，则立刻结束
    If arr Is Nothing Then
        Exit Sub
    End If

    Dim arrMaxIndex As Long
    arrMaxIndex = arr.MaxIndex
    If arrMaxIndex = -1 Then
        Exit Sub
    End If

    ' 2. 合并数据
    Dim v As Variant
    Dim i As Long
    With arr
        For i = 0 To arrMaxIndex
            ISet_Add .Item(i)
        Next
    End With
End Sub

' 将 set 内的所有元素，保存到一个对象类型的数组引用中
Public Function ISet_toObjArrayRef() As IObjArrayRef
    Set ISet_toObjArrayRef = UtilsArrayRef.createRealObjArrayRef(sortedCache.Count)

    Dim v As Variant
    For Each v In sortedCache
        ISet_toObjArrayRef.Add v
    Next
End Function

' 将 set 内的所有元素，保存到一个基础数据类型的数组引用中
Public Function ISet_toPrimitiveArrayRef() As IPrimitiveArrayRef
    Set ISet_toPrimitiveArrayRef = UtilsArrayRef.createRealPrimitiveArrayRef(sortedCache.Count)

    Dim v As Variant
    For Each v In sortedCache
        ISet_toPrimitiveArrayRef.Add v
    Next
End Function